
class Input {
  currencies
  products
  ass
  fss
  oss
};

class StrategyCore {
  config
  presto::order_gateway::china_base::OrderGatewayConfigProto og_config_;
  Date trading_date_;
  unique_ptr<MessageLoop> message_loop_;
  unique_ptr<QueueProtoFeedSubscriber> feed_subscriber_;
  unique_ptr<ChinaBaseOrderGateway> order_gateway_;
  unique_ptr<StrategyLogger> strategy_logger_;
  unique_ptr<QueueStrategyController> strategy_controller_;
  unique_ptr<strategy::Strategy> strategy_;
};


class UnaryMultifeedStrategyBase(object):
  CurrencyType = None
  ProductType = None
  LiveEnvironmentType = None
  OGName = "Unknown"

  def __init__(self, products, currencies, print_balance=False, logger=None):
    self._logger = logger or logging.getLogger('UnaryMultifeedStrategyBase')

    products = to_list(products)
    assert all([type(product) == self.ProductType for product in products]), products
    self._products = products

    currencies = to_list(currencies)
    assert all([type(currency) == self.CurrencyType for currency in currencies]), currencies
    self._currencies = sorted(to_list(currencies))
    self._print_balance = print_balance
    self._balance_print_internval = datetime.timedelta(seconds=30)

    self._feed_subsystem = None
    self._og = None

  def is_og_ready(self):
    return (self._og is not None and self._og.is_ready())

  @property
  def order_gateway(self):
    return self._og

  @property
  def feed_subsystem(self):
    return self._feed_subsystem

  def register_flow(self, ioloop, queue, flow_sub):
    self._ioloop = ioloop
    self._queue = queue
    self._flow_sub = flow_sub

  def register_feed_subsystem(self, feed_subsystem):
    self._feed_subsystem = feed_subsystem

  def register_order_gateway(self, name, order_gateway):
    self._logger.info('OG initialized: %s', name)
    if name == self.OGName:
      self._og = order_gateway
    else:
      raise ValueError('Unknown OG: %s' % name)

    if self._print_balance:
      self._start_print_balance()
    self._ioloop.add_callback(self.on_og_reset)

  def prepare(self):
    return

  def on_og_reset(self):
    raise NotImplementedError()

  @classmethod
  def _set_traits(cls, trade_subreq):
    order_cls = get_live_env_cls(trade_subreq)
    cls.CurrencyType = order_cls.OrderGateway.OGInfoType.CurrencyType
    cls.ProductType = order_cls.OrderGateway.OGInfoType.ProductType
    cls.LiveEnvironmentType = order_cls
    cls.OGName = order_cls.OrderSubReq
    assert cls.CurrencyType is not None
    assert cls.ProductType is not None

  @classmethod
  def run_live(cls,
               *,
               strategy,
               og_config_name,
               on_order_gateway_reset_callback,
               init_order_gateway_after=None,
               exit_after=None,
               fs_config=None,
               og_params=None,
               feed_checker_config=None,
               og_log_callback=None,
               slow_callback_duration=None,
               ):
    # Initialize loop
    ioloop = IOLoop.current()
    if slow_callback_duration is not None:
      assert slow_callback_duration > 0
      ioloop.asyncio_loop.slow_callback_duration = slow_callback_duration
    queue = SimpleQueue('queue')
    flow_sub = flow_subscriber.from_queue(queue)
    strategy.register_flow(ioloop, queue, flow_sub)

    # Initialize feed
    feed_subsystem = MultiFeedSubsystem(
        feed_checker_config=feed_checker_config, fs_config=fs_config)
    strategy.register_feed_subsystem(feed_subsystem)

    # Initialize OG live environment
    env = cls.LiveEnvironmentType(
        currencies=strategy._currencies,
        trade_products=strategy._products,
        og_config_name=og_config_name,
        og_params=og_params,
        init_og_after=init_order_gateway_after,
        ioloop=ioloop, queue=queue, flow_sub=flow_sub,
        og_log_callback=og_log_callback)
    env.on_order_gateway_reset_callback.append(
        strategy.register_order_gateway)

    # Start
    strategy.prepare()
    feed_subsystem.Start(ioloop, queue, flow_sub)
    env.Start()

    if exit_after is not None:
      ioloop.add_timeout(exit_after, ioloop.stop)

    try:
      ioloop.Start()
      return 0
    except KeyboardInterrupt:
      return 1
